﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows.Input;
using HeritageAcademy.Models.DataModels;
using HeritageAcademy.Models.Loaders;
using HeritageAcademy.Shared;

namespace HeritageAcademy.Models.ViewModels
{
    /// <summary>
    /// The main ViewModel class for the wizard.
    /// This class contains the various pages shown
    /// in the workflow and provides navigation
    /// between the pages.
    /// </summary>
    public class TestWizardViewModel : INotifyPropertyChanged
    {
        #region Parameters
        
        string TEST_PAGES_LOCAL_PATH = IOHelper.ApplicationExecutablePath 
                                            + HeritageAcademy.Window.Resources.Strings.Tests_Path;

        TestDataLoader _testWizardConfigLoader;
        TestResultsLoader _resultsLoader;
        TestWizardPageViewModel _currentPage;
        ReadOnlyCollection<TestWizardPageViewModel> _pages;
        
        RelayCommand _cancelCommand;
        RelayCommand _moveNextCommand;
        RelayCommand _movePreviousCommand;

        #endregion

        #region Ctor
        
        public TestWizardViewModel()
        {
            _testWizardConfigLoader = new TestDataLoader(new TestWizard(), TEST_PAGES_LOCAL_PATH);
            _resultsLoader = new TestResultsLoader(new TestResults());
            CurrentPage = Pages[0];
        }
        
        #endregion

        #region Commands

        #region CancelCommand

        /// <summary>
        /// Returns the command which, when executed, cancels the order 
        /// and causes the Wizard to be removed from the user interface.
        /// </summary>
        public ICommand CancelCommand
        {
            get
            {
                if (_cancelCommand == null)
                    _cancelCommand = new RelayCommand(() => this.CancelOrder());

                return _cancelCommand;
            }
        }

        void CancelOrder()
        {
            this.OnRequestClose();
        }

        #endregion

        #region MovePreviousCommand

        /// <summary>
        /// Returns the command which, when executed, causes the CurrentPage 
        /// property to reference the previous page in the workflow.
        /// </summary>
        public ICommand MovePreviousCommand
        {
            get
            {
                if (_movePreviousCommand == null)
                    _movePreviousCommand = new RelayCommand(
                        () => this.MoveToPreviousPage(),
                        () => this.CanMoveToPreviousPage);

                return _movePreviousCommand;
            }
        }

        public bool CanMoveToPreviousPage
        {
            get { return 0 < CurrentPageIndex; }
        }

        public void MoveToPreviousPage()
        {
            if (this.CanMoveToPreviousPage)
                CurrentPage = Pages[CurrentPageIndex - 1];
        }

        #endregion

        #region MoveNextCommand

        /// <summary>
        /// Returns the command which, when executed, causes the CurrentPage 
        /// property to reference the next page in the workflow.  If the user
        /// is viewing the last page in the workflow, this causes the Wizard
        /// to finish and be removed from the user interface.
        /// </summary>
        public ICommand MoveNextCommand
        {
            get
            {
                if (_moveNextCommand == null)
                    _moveNextCommand = new RelayCommand(
                        () => this.MoveToNextPage(),
                        () => this.CanMoveToNextPage);

                return _moveNextCommand;
            }
        }

        public bool CanMoveToNextPage
        {
            get { return this.CurrentPage != null && this.CurrentPage.IsValid(); }
        }

        public void MoveToNextPage()
        {
            if (this.CanMoveToNextPage)
            {
                if (this.CurrentPageIndex < Pages.Count - 1)
                    CurrentPage = Pages[CurrentPageIndex + 1];
                else
                    this.OnRequestClose();
            }
        }

        #endregion

        #endregion

        #region Properties

        /// <summary>
        /// Returns the page ViewModel that the user is currently viewing.
        /// </summary>
        public TestWizardPageViewModel CurrentPage
        {
            get { return _currentPage; }
            private set
            {
                if (value == _currentPage)
                    return;

                if (_currentPage != null)
                    _currentPage.IsCurrentPage = false;

                _currentPage = value;

                if (_currentPage != null)
                    _currentPage.IsCurrentPage = true;

                OnPropertyChanged("CurrentPage");
                OnPropertyChanged("IsOnLastPage");
            }
        }

        /// <summary>
        /// Returns true if the user is currently viewing the last page 
        /// in the workflow.  This property is used by TestWizardView
        /// to switch the Next button's text to "Finish" when the user
        /// has reached the final page.
        /// </summary>
        public bool IsOnLastPage
        {
            get { return CurrentPageIndex == Pages.Count - 1; }
        }

        /// <summary>
        /// Returns true if the user is currently viewing the last page 
        /// in the workflow.  This property is used by TestWizardView
        /// to switch the Next button's text to "Finish" when the user
        /// has reached the final page.
        /// </summary>
        public bool IsOnFirstPage
        {
            get { return CurrentPageIndex == 0; }
        }

        /// <summary>
        /// Returns a read-only collection of all page ViewModels.
        /// </summary>
        public ReadOnlyCollection<TestWizardPageViewModel> Pages
        {
            get
            {
                if (_pages == null) CreatePages();
                return _pages;
            }
        }

        /// <summary>
        /// Get overall number of pages in test
        /// </summary>
        public string PageCount
        {
            get { return Pages.Count.ToString(); }
        }

        #endregion

        #region Private Helpers

        void CreatePages()
        {
            int number = 0;
            var pages = new List<TestWizardPageViewModel>();
            foreach (var page in _testWizardConfigLoader.Model.TestPage)
            {
                TestWizardPageViewModel vm = new TestWizardPageViewModel(_resultsLoader);
                vm.Source = TEST_PAGES_LOCAL_PATH + page.Source;
                vm.Number = ++number;
                pages.Add(vm);
            }
            _pages = new ReadOnlyCollection<TestWizardPageViewModel>(pages);
        }
      
        int CurrentPageIndex
        {
            get
            {
                if (CurrentPage == null)
                {
                    Debug.Fail("Why is the current page null?");
                    return -1;
                }

                return Pages.IndexOf(this.CurrentPage);
            }
        }

        void OnRequestClose()
        {
            EventHandler handler = this.RequestClose;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        #endregion

        #region Public Methods

        public void InitializeCurentPageAnswers(Control ctrl)
        {
            _currentPage.GetAvailableAnswers(ctrl);
        }

        #endregion

        #region Events

        /// <summary>
        /// Raised when the wizard should be removed from the UI.
        /// </summary>
        public event EventHandler RequestClose;

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
    }
}
